module Control
  require 'java'
  require 'rplib.jar'
  include_class 'processing.core.PApplet'
  include_class 'IPAppletAdapter' 
  include_class 'java.awt.Insets'
  include_package 'javax.swing'
  GridBagLayout = java.awt.GridBagLayout
  GridBagConstraints = java.awt.GridBagConstraints
  
  class ActionListener
    def initialize(&callback)
      @callback = callback
    end
    def actionPerformed(e)
      @callback.call
    end
  end  
  
  class ChangeListener
    def initialize(&callback)
      @callback = callback
    end
    def stateChanged(e)
      @callback.call
    end
  end
  
  class EventQueue
    include IPAppletAdapter
    
    def initialize
      super()
      @listeners = []
    end
    
    def add_callback(&callback)
      @listeners << callback
    end
    
    def pre
      @listeners.each {|f| f.call}
      @listeners.clear
    end
  end
  
  class DisposeListener
    include IPAppletAdapter
    
    def initialize(&callback)
      super()
      @callback = callback
    end
    
    def dispose
      @callback.call
    end
  end
  
  def self.__add_attribute(name, &block)
    define_method(name, &block)
  end
  
  def create_frame
    unless @frame
      @num_component = 0
      @frame = JFrame.new('attributes')
      @frame.setLocation([128, width].max, 0) unless $irp.fullscreen?
      @frame.resizable = false
      hbox = Box.createHorizontalBox
      hbox.add(Box.createHorizontalStrut(20))
      vbox = Box.createVerticalBox
      vbox.add(Box.createVerticalStrut(20))
      @box = JPanel.new
      @layout = GridBagLayout.new
      @box.setLayout(@layout)
      vbox.add(@box)
      vbox.add(Box.createVerticalStrut(20))
      hbox.add(vbox)
      hbox.add(Box.createHorizontalStrut(20))
      @frame.add(hbox)
      
      @queue = EventQueue.new
      registerPre(@queue)
      
      dl = DisposeListener.new do
        @frame.visible = false
        @frame.dispose
      end
      registerDispose(dl)
    end
  end
  
  def slider(name, default = 1, min = 0, max = 2) 
    default = val = [[min, default].max, max].min
    step = 1000
    s = JSlider.new(0, step, step * (default - min) / (max - min)) 
    add_component(name.to_s, s, Insets.new(0, 0, 0, 0))
           
    flen = Math.log10(max - min).round - 3
    flen = -[0, flen].min                 
    tf = JTextField.new(sprintf("%.#{flen}f", default))
    tf.setHorizontalAlignment(JTextField::CENTER)
    tf.setEditable(false)
    tf.setBorder(nil)
    tf.setBackground(UIManager.getColor("Label.background"))
    tf.setFont(UIManager.getFont("Label.font"))
    add_component(nil, tf)  
    
    Control.__add_attribute(name) do
      val
    end
    
    s.addChangeListener(ChangeListener.new do
      val = PApplet.lerp(min, max, s.getValue / step.to_f) 
      tf.setText(sprintf("%.#{flen}f", val))
    end)
  end
  
  def text_field(name, default = '')
    t = JTextField.new(default)
    Control.__add_attribute(name) do
      t.text
    end
    add_component(name.to_s, t)
  end
  
  def check_box(name, default = false)
    c = JCheckBox.new(name.to_s, default)
    Control.__add_attribute(name) do
      c.isSelected
    end
    add_component(nil, c)
  end
  
  def button(name)
    b = JButton.new(name.to_s)
    al = ActionListener.new do
      @queue.add_callback {send(name)}
    end
    b.addActionListener(al)
    add_component(nil, b)
  end
  
  def add_component(name, component, insets = nil)
    insets = Insets.new(0, 0, 10, 0) unless insets     
    create_frame    
    
    gbc = GridBagConstraints.new
    if name
      gbc.gridx = 0
      gbc.gridy = @num_component
      gbc.anchor = GridBagConstraints::EAST  
      gbc.insets = insets 
      label = JLabel.new(name + ' ', SwingConstants::RIGHT)
      @layout.setConstraints(label, gbc)
      @box.add(label)
    end
    gbc.gridx = 1
    gbc.gridy = @num_component
    gbc.anchor = GridBagConstraints::WEST
    gbc.fill = GridBagConstraints::HORIZONTAL 
    gbc.insets = insets
    @layout.setConstraints(component, gbc)
    @box.add(component)
    
    @frame.pack
    @frame.setVisible(true)
    @num_component += 1    
  end 
end